//! @file   lc_multi_reader_lock.cpp
//! @brief  
//! @author Sayan Chaliha
//! @date   September, 2011
//!
//! 
#include <lc_multi_reader_lock.h>
#include <lc_system.h>

namespace cyan {
  MultiReaderLock::MultiReaderLock() {
    Int count = 0;
    Int returnCode = ::pthread_rwlock_init(&this->lock_, nullptr);

    while ((returnCode != 0) && (returnCode == EAGAIN ||
        returnCode == EBUSY)) {
      returnCode = ::pthread_rwlock_init(&this->lock_, nullptr);
      if (returnCode == 0 || ++count == MAX_RETRIES)
        break;
      System::sleep(1);
    }
    //TODO: Log system error
  }

  MultiReaderLock::~MultiReaderLock() {
    Int count = 0;
    Int returnCode = ::pthread_rwlock_destroy(&this->lock_);

    while ((returnCode != 0) && (returnCode == EAGAIN ||
        returnCode == EBUSY)) {
      this->unlock();
      returnCode = ::pthread_rwlock_destroy(&this->lock_);
      if (returnCode == 0 || ++count == MAX_RETRIES)
        break;
      System::sleep(1);
    }
    //TODO: Log system error
  }

  void MultiReaderLock::readLock() {
    Int returnCode = ::pthread_rwlock_rdlock(&this->lock_);

    if (returnCode != 0)
      System::throwSystemException(returnCode);
  }

  void MultiReaderLock::writeLock() {
    Int returnCode = ::pthread_rwlock_wrlock(&this->lock_);

    if (returnCode != 0)
      System::throwSystemException(returnCode);
  }

  Bool MultiReaderLock::tryReadLock() {
    Int returnCode = ::pthread_rwlock_tryrdlock(&this->lock_);

    if (returnCode == 0)
      return true;
    else if (returnCode == EBUSY)
      return false;

    System::throwSystemException(returnCode);
  }

  Bool MultiReaderLock::tryWriteLock() {
    Int returnCode = ::pthread_rwlock_trywrlock(&this->lock_);

    if (returnCode == 0)
      return true;
    else if (returnCode == EBUSY)
      return false;

    System::throwSystemException(returnCode);
  }

  void MultiReaderLock::unlock() {
    Int returnCode = ::pthread_rwlock_unlock(&this->lock_);

    if (returnCode != 0)
      System::throwSystemException(returnCode);    
  }

  WriteGuard::WriteGuard(MultiReaderLock& lock) : lock_(&lock) {
    this->lock_->writeLock();
  }

  WriteGuard::~WriteGuard() {
    this->lock_->unlock();
  }

  ReadGuard::ReadGuard(MultiReaderLock& lock) : lock_(&lock) {
    this->lock_->readLock();
  }

  ReadGuard::~ReadGuard() {
    this->lock_->unlock();
  }
}
